#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#include "driver/uart.h"
#include "string.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "ble_spp_server_demo.h"

//add
#include "ble_data_handle.h"
#include "esp_partition.h"
#include "flash.h"
#include "esp32/rom/md5_hash.h"
#include "mbedtls/md5.h"

#define TMP_FILENAME "tmp"

esp_err_t flash_firmwart_erase(size_t dst_offset)
{
    const esp_partition_t *tmp_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, TMP_FILENAME);
    assert(tmp_partition != NULL);
    ESP_ERROR_CHECK(esp_partition_erase_range(tmp_partition, 0, dst_offset));
    return ESP_OK;
}

esp_err_t flash_firmwart_write(size_t dst_offset, const void *src, size_t size)
{
    esp_partition_t *tmp_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, TMP_FILENAME);
    assert(tmp_partition != NULL);
    ESP_ERROR_CHECK(esp_partition_write(tmp_partition, dst_offset, (const void *)src, size));
    return ESP_OK;
}

esp_err_t flash_firmwart_read(size_t dst_offset, void *dst, size_t size)
{
    esp_partition_t *tmp_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, TMP_FILENAME);
    assert(tmp_partition != NULL);
    ESP_ERROR_CHECK(esp_partition_read(tmp_partition, dst_offset, (void *)dst, size));
    return ESP_OK;
}

#define INFO_FILENAME "info"

esp_err_t flash_firmwart_info_erase()
{
    const esp_partition_t *tmp_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, INFO_FILENAME);
    assert(tmp_partition != NULL);
    ESP_ERROR_CHECK(esp_partition_erase_range(tmp_partition, 0, tmp_partition->size));
    return ESP_OK;
}

esp_err_t flash_firmwart_info_write(size_t dst_offset, const void *src, size_t size)
{
    esp_partition_t *tmp_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, INFO_FILENAME);
    assert(tmp_partition != NULL);
    ESP_ERROR_CHECK(esp_partition_write(tmp_partition, dst_offset, (const void *)src, size));
    return ESP_OK;
}

esp_err_t flash_firmwart_info_read(size_t dst_offset, void *dst, size_t size)
{
    esp_partition_t *tmp_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, INFO_FILENAME);
    assert(tmp_partition != NULL);
    ESP_ERROR_CHECK(esp_partition_read(tmp_partition, dst_offset, (void *)dst, size));
    return ESP_OK;
}

#define INFO_PARTITION_PAGE (1024 * 4)

#define WRITE_SIZE (512)
static const int RX_BUF_SIZE = 1024;

esp_err_t flash_chack_sum(uint8_t *sum_out)
{
    uint8_t firmwart_info[20];
    uint32_t firmwart_len;
    uint8_t firmwart_md5[16];

    flash_firmwart_info_read(0, firmwart_info, 20);

    firmwart_len = (firmwart_info[0] << 24 | firmwart_info[1] << 16 | firmwart_info[2] << 8 | firmwart_info[3] << 0);
    memcpy(firmwart_md5, &firmwart_info[4], 16);

    if (firmwart_len > FIRMWART_SIZE)
    {
        ESP_LOGE("FIRMWART SUM", "不存在固件\n");
        return;
    }

    ESP_LOGW("FIRMWART SUM", "固件长度：%u\n", firmwart_len);

    //firmwart
    uint8_t *firmwart_data = (uint8_t *)malloc(RX_BUF_SIZE + 1);
    assert(firmwart_data != NULL);

    uint32_t md5_count = 0;
    uint32_t now_md5_len = 0;
    uint32_t md5_len = firmwart_len;
    struct MD5Context md5;
    MD5Init(&md5);
    while (md5_len)
    {
        if (md5_len > WRITE_SIZE)
            now_md5_len = WRITE_SIZE;
        else
            now_md5_len = md5_len;

        flash_firmwart_read(md5_count * WRITE_SIZE, firmwart_data, now_md5_len);
        MD5Update(&md5, firmwart_data, now_md5_len);

        md5_count++;
        md5_len -= now_md5_len;
    }
    uint8_t local_file_md5[16];
    memset(local_file_md5, 0, sizeof(local_file_md5));
    MD5Final(local_file_md5, &md5);
    free(firmwart_data);

    printf("\r\n信息记录 MD5: ");
    for (size_t i = 0; i < 16; i++)
        printf("%02X ", firmwart_md5[i]);
    printf("\r\n");
    

    printf("\r\n实际固件 MD5: ");
    for (size_t i = 0; i < 16; i++)
        printf("%02X ", local_file_md5[i]);
    printf("\r\n");

    for (int i = 0; i < 16; i++)
    {
        if (firmwart_md5[i] != local_file_md5[i])
        {
            ESP_LOGE("FIRMWART SUM", "固件检验失败\n");
            return ESP_FAIL;
        }
    }

    memcpy(sum_out,firmwart_md5,16);
    ESP_LOGW("FIRMWART SUM", "固件检验通过\n");
    return ESP_OK;
}




static nvs_handle burn_nvs_handle;



esp_err_t burn_nvs_init(void)
{
    esp_err_t result = ESP_FAIL;
    ESP_ERROR_CHECK(nvs_open("demoks_ceye", NVS_READWRITE, &burn_nvs_handle));

    return ESP_OK;
}

esp_err_t set_number_limit(uint32_t limit)
{
    ESP_ERROR_CHECK(nvs_set_u32(burn_nvs_handle, "_number_limit", limit));
    return ESP_OK;
}

esp_err_t get_number_limit(uint32_t *limit)
{
    return nvs_get_u32(burn_nvs_handle, "_number_limit", limit);
}



